/**************************************************************************************
 *  Copyright (c)  2010  Leszek Karcz, Changmin Bae.
 *  Permission is granted to copy, distribute and/or modify this document
 *  under the terms of the GNU Free Documentation License, Version 1.3
 *  or any later version published by the Free Software Foundation;
 *  with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
 *  A copy of the license is included in the section entitled "GNU
 *  Free Documentation License". 
 **************************************************************************************/
package data.tileables;
import data.*;
import data.missions.Mission;

/**************************************************************************************
 * Author(s): Leszek Karcz, Changmin Bae
 * Base structure for all player controllable elements that can be put inside a tile
 **************************************************************************************/
public abstract class Controllable extends Tileable
{

	public static final int FOOD = 0, METAL = 1, ENERGY = 2; 
	private int [] resourceRate = new int[3];
	private float [] resourcePer = new float [3];
	
	private boolean alive=true;
	private int modStats;
	private int displayLevel;
	private int ID;
	private int supertypeID; //ID of unit/structure
	protected Player player;
	private int[] upkeep;
	private double health;
	private int maxHealth;
	private int armor;
	private int offensiveDamage;
	private int defensiveDamage;
	private int visionRadius;
	private String icon;
	private boolean state; //false - power down; true - power up
	private int numberOfCommands=0;
	private int selectedCommand=0;
	private int commandMode=0;
	private int commandValue=0;
	protected LinkedList<Mission> missionQueue;

	
	/**************************************************************************************
	 * Author(s): Leszek Karcz, Changmin Bae
	 * Controllable constructor
	 **************************************************************************************/
	public Controllable(int ID, int supertypeID, Player player)
	{
		super();
		this.setType("Controllable");
		this.setIcon("");
		this.setDisplayLevel(0);
		this.ID=ID;
		this.supertypeID=supertypeID;
		this.player=player;
		this.state=true;
		this.setStats(0, 0, 0, 0, 0, 0);
		this.setUpkeep(new int[]{0,0,0});
		this.missionQueue = new LinkedList<Mission>();
		this.modStats = 1;
		
		this.setAllocationRate( 1, 1, 1);
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * executes per tick instructions
	 **************************************************************************************/
	public void execute(){
		if (health == 0)
			die();
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * executes instructions regarding currently selected command
	 **************************************************************************************/
	public abstract boolean enqueueSelectedCommand(String mode, Map m, int x, int y);
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * returns list of possible commands
	 **************************************************************************************/
	public abstract String[] getCommandsText();
	
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * returns number of commands
	 **************************************************************************************/
	public int getNumberOfCommands()
	{
		return this.numberOfCommands;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * resets the command mode
	 **************************************************************************************/
	public void resetCommandMode()
	{
		this.setCommandMode(0);
		this.setCommandValue(0);
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * gets the command mode
	 **************************************************************************************/
	public int getCommandMode()
	{
		return this.commandMode;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * sets the command mode
	 **************************************************************************************/
	protected void setCommandMode(int commandMode)
	{
		this.commandMode=commandMode;
		this.resetCommand();
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * gets the command value
	 **************************************************************************************/
	public int getCommandValue()
	{
		return this.commandValue;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * sets the command value
	 **************************************************************************************/
	protected void setCommandValue(int commandValue)
	{
		this.commandValue=commandValue;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * sets number of commands
	 **************************************************************************************/
	protected void setNumberOfCommands(int numberOfCommands)
	{
		this.numberOfCommands=numberOfCommands;
	}
	
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * returns base upkeep
	 **************************************************************************************/
	public int[] getUpkeep()
	{
		int [] base = this.upkeep.clone();
		if(this.getState() == 0){
			base[FOOD] *= 0.25;
			base[METAL] *= 0.25;
			base[ENERGY] *= 0.25;
		}
		
		return base;	
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * sets price
	 **************************************************************************************/
	protected void setUpkeep(int[] upkeep)
	{
		this.upkeep=upkeep;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * returns selected command
	 **************************************************************************************/
	public int getSelectedCommand()
	{
		return this.selectedCommand;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * reset command
	 **************************************************************************************/
	public void resetCommand()
	{
		this.selectedCommand=0;
	}
		
	/**************************************************************************************
	 * Author(s): Changmin Bae
	 * gets current mission's name
	 **************************************************************************************/
	public String getCurrentMissionName()
	{
		if(missionQueue.getSize()>0)
		{
		return missionQueue.peekFirst().getName();
		}
		else
		{
			return "idle";
		}
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * selects next command
	 **************************************************************************************/
	public void selectNextCommand()
	{
		this.selectedCommand++;
		if(this.selectedCommand>this.getNumberOfCommands()-1)
		{
			this.selectedCommand=0;
		}		
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * selects previous command
	 **************************************************************************************/
	public void selectPreviousCommand()
	{
		this.selectedCommand--;
		if(this.selectedCommand<0)
		{
			this.selectedCommand=this.getNumberOfCommands()-1;
		}		
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Set the stats of the controllable: HP/armor/baseUpkeep/etc.
	 **************************************************************************************/
	protected void setStats(double health, int maxHealth, int armor, int offensiveDamage, int defensiveDamage, int visionRadius)
	{
		this.health=health;
		this.maxHealth=maxHealth;
		this.armor=armor;
		this.offensiveDamage=offensiveDamage;
		this.defensiveDamage=defensiveDamage;
		this.visionRadius=visionRadius;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Return the base stats of the controllable: HP/armor/Upkeep/etc.
	 **************************************************************************************/
	public int[] getBaseStats()
	{
		return new int[]{	(int)this.health,
							this.maxHealth,
							this.armor,
							this.offensiveDamage,
							this.defensiveDamage,
							this.getVisionRadius()};
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Return the max health
	 **************************************************************************************/
	public int getMaxHealth()
	{
		return this.maxHealth+this.player.getTechnology(this.getType(), "Health");
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Return the stats of the controllable: HP/armor/Upkeep/etc. with applied bonuses
	 **************************************************************************************/
	public int[] getStats()
	{
		return new int[]{	(int)this.health,
							this.getMaxHealth(),
							this.armor+this.player.getTechnology(this.getType(), "Armor Strength"),
							this.offensiveDamage,
							this.defensiveDamage,
							this.getVisionRadius()};
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Returns the vision radius of controllable
	 **************************************************************************************/
	public int getVisionRadius()
	{
		return this.visionRadius+this.player.getTechnology(this.getType(), "Armor Strength");
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Return the state: 0 - power-down; 1 - power-up
	 **************************************************************************************/
	public int getState()
	{
		if(this.state)
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Return the state: 0 - power-down; 1 - power-up
	 **************************************************************************************/
	public String getStateText()
	{
		if(this.state)
		{
			return "Active";
		}
		else
		{
			return "Power-down";
		}
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Return the stats of the controllable: HP/armor/Upkeep/etc.
	 **************************************************************************************/
	public abstract String[] getStatsText();

	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * modifies the health of controllable by a value
	 **************************************************************************************/
	public void changeHealth(double value)
	{		
		this.health+=value;
		if(this.health<=0)
		{
			this.health=0;
			this.die();
		}
		else if(this.health>getMaxHealth())
		{
			this.health=getMaxHealth();
		}
	}
	
	/**************************************************************************************
	 * Author(s): Eric
	 * Called when damage from battle
	 **************************************************************************************/
	public void takeDamage(int value)
	{		
		if(value > this.getStats()[2])
			changeHealth(-(value - this.getStats()[2]));
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * modifies the armor of controllable
	 **************************************************************************************/
	public void changeArmor(int value)
	{
		this.armor+=value;
		if(this.armor<0)
		{
			this.armor=0;
		}
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Returns health percentage as integer
	 **************************************************************************************/
	public int getHealthPercentage()
	{
		return (int)(100*this.health/this.getMaxHealth());
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Returns ID of the controllable
	 **************************************************************************************/
	public int getID()
	{
		return this.ID;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Returns supertypeID of the controllable
	 **************************************************************************************/
	public int getSupertypeID()
	{
		return this.supertypeID;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Returns ID of the player owning this controllable
	 **************************************************************************************/
	public int getPlayerID()
	{
		return this.player.getID();
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * get image file  name for small representation of the controllable
	 **************************************************************************************/
	public String getIcon()
	{
		return this.icon;
	}	
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * get icon file  name, highlight file name and health percentage
	 **************************************************************************************/
	public String[] getIconData(int playerID)
	{
		return new String[]{((this.getPlayerID()==playerID) && (this.equals(this.player.getSelected())) ? "pselected_icon_h" : "p"+this.getPlayerID()+"_icon_h"),this.getIcon(),""+this.getHealthPercentage()};
	}	
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * get icon file  name, highlight file name
	 **************************************************************************************/
	public abstract String[] getImageData(int playerID);
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * set image icon name
	 **************************************************************************************/
	protected void setIcon(String icon)
	{
		this.icon=icon;
	}	
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * set display level
	 **************************************************************************************/
	protected void setDisplayLevel(int displayLevel)
	{
		this.displayLevel=displayLevel;
	}	
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * get display level
	 **************************************************************************************/
	public int getDisplayLevel()
	{
		return this.displayLevel;
	}	
	
	/**************************************************************************************
	 * Author(s): Changmin Bae
	 * Changes state of the unit | s = true is power | up s = false is power down
	 **************************************************************************************/
	public void setState(boolean s)
	{
		state = s;
	}
	
	/**************************************************************************************
	 * Author(s): Changmin Bae
	 * Adds a mission m to the missionQueue
	 **************************************************************************************/
	public void addMission(Mission m)
	{
		missionQueue.addBack(m);
	}
	
	/**************************************************************************************
	 * Author(s): Changmin Bae
	 * Clears the current missionQueue
	 **************************************************************************************/
	public void clearMissions()
	{
		while (!missionQueue.isEmpty())
		{
			missionQueue.removeFirst();
		}
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Returns whether the controllable is alive
	 **************************************************************************************/
	public void die()
	{
		this.alive=false;
		this.clearMissions();
	}

	public boolean isAlive()
	{
		return this.alive;
	}
	
	/**************************************************************************************
	 * Author(s): Eric
	 * sets the rates of allocations, both the minimal they need to survive and percent allocation
	 **************************************************************************************/
	protected void setAllocationRate(float metal, float food, float energy){
		resourcePer[FOOD] = food;
		resourcePer[METAL] = metal;
		resourcePer[ENERGY] = energy;
	}
	
	/**************************************************************************************
	 * Author(s): Eric
	 * get the percentages that it ask the player for resources
	 **************************************************************************************/
	protected float [] getAllocationRate(){return resourcePer.clone();}
	
	/**************************************************************************************
	 * Author(s): Eric
	 * Askes the player for amount of allocations
	 **************************************************************************************/
	protected void allocate(){
		
		int [] resourcesNeeded = this.getUpkeep();
		
		int [] allocated = player.getAllocation(resourcesNeeded, getAllocationRate());
		
		int needed = (resourcesNeeded[FOOD] - allocated[FOOD]) + (resourcesNeeded[METAL] - allocated[METAL]) + (resourcesNeeded[ENERGY] - allocated[ENERGY]);
		int total = resourcesNeeded[FOOD]+ resourcesNeeded[METAL]+resourcesNeeded[ENERGY];
		
		if(needed > 0)
		{
			double decrease=-(this.getMaxHealth()*((float)needed)/total/10);
			if(decrease>-1)
			{
				decrease=-1;
			}
			changeHealth(decrease);/**/
		}
	}

	
	
}
